// Copyright (C) 2011 by Christopher Diggins
// Usage is permitted under the terms of the MIT License 1.0. 		

/** \file 
	This file contains the implementation of functions directly responsible for 
	evaluating opcodes.
	\see \ref opeval
*/

/** Opcode evaluator function. */
void vm_eval(VM* vm, byte code) 
{
	Val* tmp;

	switch (code)
	{
	case _noop:		break;
	case _null:		vm_push(NULL); break;
	//case _zero:		vm_push_int(0); break;
	//case _zerof:	vm_push_float(0.0f); break;
	//case _zerod:	vm_push_double(0.0); break;
	//case _one:		vm_push_int(1); break;
	//case _two:		vm_push_int(2); break;
	//case _neg1:		vm_push_int(-1); break;
	case _gc:		vm_gc(vm);  break;
	case _newobj:	vm_push(obj_new()); break;
	case _call:		vm_call_impl(vm, vm_pull());  break;
	case _tail:		vm_tailcall_impl(vm, vm_pull()); break;
	case _ret:		vm_tailcall_impl(vm, vm_pull_aux()); break;
	case _yield:	vm_tailcall_impl(vm, vm_pull_aux()); break;
	case _while:	vm_while_impl(vm, vm_pull()); break;
	case _toaux:	vm_push_aux(vm_pull()); break;
	case _fromaux:	vm_push(vm_pull_aux());break;
	case _popaux:	vm_pop_aux(); break;
	case _kbyte:	vm_push(vm_constant(vm_fetch_byte())); break;
	case _kshort:	vm_push(vm_constant(vm_fetch_short())); break;
	case _klong:	vm_push(vm_constant(vm_fetch_long())); break;
	case _fbyte:	vm_push(vm_function(vm_fetch_byte())); break;
	case _fshort:	vm_push(vm_function(vm_fetch_short())); break;
	case _flong:	vm_push(vm_function(vm_fetch_long())); break;
	case _byte:		vm_push_int(vm_fetch_byte()); break;
	case _short:	vm_push_int(vm_fetch_short()); break;
	case _long:		vm_push_int(vm_fetch_long()); break;
	case _float:	vm_push_float(vm_fetch_float()); break;
	case _double:	vm_push_double(vm_fetch_double()); break;
	case _dup:		vm_push(val_dup(vm_top())); break;
	case _dupN:		vm_dup_impl(vm, vm_fetch_byte()); break;
	case _pop:		vm_pop(); break;
	case _popN:		{ int n = vm_fetch_byte(); while (n--) vm_pop(); } break;
	case _getN:		vm_push(val_dup(vm_at(vm_fetch_byte()))); break;
	case _setN:		vm_swap_impl(vm, 0, vm_fetch_byte()); vm_pop(); break; 
	case _swap:		vm_swap_impl(vm, 0, 1); break;
	case _swapN:	vm_swap_impl(vm, 0, vm_fetch_byte());  break;
	case _swapNM:	vm_swap_impl(vm, vm_fetch_byte(), vm_fetch_byte()); break;
	case _buryN:	vm_bury_impl(vm, vm_fetch_byte()); break;
	case _digN:		vm_dig_impl(vm, vm_fetch_byte()); break;
	case _add:		EVALUATE_BINARY_NUMERIC_OP_STACK(+=); break;
	case _sub:		EVALUATE_BINARY_NUMERIC_OP_STACK(-=); break;
	case _mul:		EVALUATE_BINARY_NUMERIC_OP_STACK(*=); break;
	case _div:		EVALUATE_BINARY_NUMERIC_OP_STACK(/=); break;
	case _mod:		EVALUATE_BINARY_INTEGER_OP_STACK(%=); break;
	case _shl:		EVALUATE_BINARY_INTEGER_OP_STACK(<<=); break;
	case _shr:		EVALUATE_BINARY_INTEGER_OP_STACK(>>=); break;
	case _and:		EVALUATE_BINARY_INTEGER_OP_STACK(&=); break;
	case _or:		EVALUATE_BINARY_INTEGER_OP_STACK(|=); break;
	case _xor:		EVALUATE_BINARY_INTEGER_OP_STACK(^=); break;
	case _not:		EVALUATE_UNARY_INTEGER_OP_STACK(!); break;
	case _compl:	EVALUATE_UNARY_INTEGER_OP_STACK(~); break;
	case _inc:		EVALUATE_UNARY_INTEGER_OP_STACK(++); break;
	case _dec:		EVALUATE_UNARY_INTEGER_OP_STACK(--); break;
	case _gt:		EVALUATE_COMPARISON_OP_STACK(>); break;
	case _lt:		EVALUATE_COMPARISON_OP_STACK(<); break;
	case _gteq:		EVALUATE_COMPARISON_OP_STACK(>=); break;
	case _lteq:		EVALUATE_COMPARISON_OP_STACK(<=); break;
	case _eq:		EVALUATE_COMPARISON_OP_STACK(==); break;
	case _neq:		EVALUATE_COMPARISON_OP_STACK(!=); break;
	case _apush:	obj_push(vm_second(), vm_pull()); break;
	case _apop:		vm_push(obj_pull(vm_top())); break;
	case _aswap:	{
		int n = vm_pull_int();
		tmp = vm_top();
		vm_top() = obj_at(vm_second(), n);
		obj_at(vm_second(), n) = tmp;
		} break;
	case _sget:		vm_push(seq_get(vm, vm_pull_int(), vm_top())); break;
	case _sset:		seq_set(vm, vm_pull_int(), vm_pull(), vm_top()); break;
	case _scat:		vm_push(seq_cat(vm_pull(), vm_pull())); break;
	case _scount:	vm_push_int(obj_count(vm_top())); break;
	case _sclone:	vm_push(seq_clone(vm, vm_top())); break;
	case _if:		if (!vm_pull_int()) vm_swap_impl(vm, 0, 1); vm_pop(); break;
	case _ift:		if (vm_pull_int()) vm_call_impl(vm, vm_pull()); else vm_pop(); break;
	case _trycatch: 
		vm_push_aux(vm_pull());  
		tmp = vm_pull(); 
		vm_push(&marker);
		vm_push_aux(&marker);
		vm_call_impl(vm, tmp);
		break;
	case _throw: 
		vm_throw(vm_pull());
		break;
	case _tblnew:
		tmp = table_new();
		val_add_ref(tmp);
		vm_push(tmp);
		break;
	case _tbladd: {
		Val* key = vm_pull();
		Val* val = vm_pull();
		val_del_ref(key);
		table_add(vm_top(), val_to_int(key), val);
		break; }
	case _tblfind: {
		Val* key = vm_pull();
		vm_push(table_find(vm_top(), (uint)key)->val);
		val_del_ref(key);
		break; }
	case _store: {
		Val* store = val_new(StoreType);
		vm_swap_impl(vm, 0, 1);
		tmp = vm_pull(); 
		serialize_vm(vm, store, 1);
		vm_pop(); 
		vm_push(store);
		vm_call_impl(vm, tmp);
		break; }
	case _restore: 
		vm_restore(vm, vm_pull());
		vm_call_impl(vm, vm_pull()); 
		break;
	case _tostr: vm_push(val_to_str(vm_top())); break;
	case _vcpy: vm_push(vec_copy(vm_top())); break;
	case _vclr: vec_clr(vm_top()); break;
	case _vbyte: vm_push(vec_new(VByteType, vm_pull_int())); break;
	case _vint: vm_push(vec_new(VIntType, vm_pull_int())); break;
	case _vflt: vm_push(vec_new(VFloatType, vm_pull_int())); break;
	case _vdbl: vm_push(vec_new(VDoubleType, vm_pull_int())); break;
	default:
		assert(!"Unrecognized opcode");
	}
}
